﻿using System;
using System.Collections.Generic;
using My.Ioc;

namespace ObserverUsage
{
    #region ObjectObserver Types

    public class ServiceConsumer
    {
        Service _service;

        public ServiceConsumer(Service service)
        {
            _service = service;
        }
    }

    public class Service
    {
    }

    #endregion

    #region ObjectCollectionObserver Types

    public class Tree
    {
        readonly IList<Node> _childNodes;

        public Tree(IList<Node> childNodes)
        {
            _childNodes = new List<Node>(childNodes);
        }

        public IList<Node> ChildNodes
        {
            get { return _childNodes; }
        }
    }

    public abstract class Node
    {
        string _name;

        public string Name
        {
            get
            {
                _name = _name ?? GetType().Name;
                return _name;
            }
        }
    }

    public class Node1 : Node
    {
    }

    public class Node2 : Node
    {
    }

    public class Node3 : Node
    {
    }

    public class Node4 : Node
    {
    }

    #endregion

    class Program
    {
        private static IObjectContainer _container;

        static void Main(string[] args)
        {
            _container = new ObjectContainer(false);
            RunObjectObserverTest();
            RunObjectCollectionObserverTest();

            Console.ReadLine();
        }

        #region ObjectObserver

        static void RunObjectObserverTest()
        {
            IObjectRegistration<Service> serviceRegistration;
            IObjectObserver<ServiceConsumer> consumerObserver;
            IObjectObserver<Service> serviceObserver;

            // Register and commit
            _container.Register<Service>().Return(out serviceRegistration);
            _container.Register<ServiceConsumer>();
            _container.CommitRegistrations();

            // Try to get the ObjectObserver and hook events
            if (_container.TryGetObserver(out serviceObserver))
                serviceObserver.Changed += OnObjectBuilderChangedForService;
            if (_container.TryGetObserver(out consumerObserver))
                consumerObserver.Changed += OnObjectBuilderChangedForConsumer;

            // Try to resolve an instance of ServiceConsumer to build the relationship between the 
            // ServiceConsumer and Service, so that if we unregistered the Service, not only the 
            // service observer, but also the consumer observer, will receive the notification.
            var consumer = _container.Resolve<ServiceConsumer>();

            // Unregister the Service. The observers should receive the notification later.
            _container.Unregister(serviceRegistration);

            // Dispose the observers
            consumerObserver.Dispose();
            serviceObserver.Dispose();
        }

        static void OnObjectBuilderChangedForService(ObjectBuilderChangedEventArgs args)
        {
            if (args.ChangeMode == ObjectBuilderChangeMode.Activate)
                Console.WriteLine("The Service is activated...");
            else if (args.ChangeMode == ObjectBuilderChangeMode.Deactivate)
                Console.WriteLine("The Service is deactivated...");
        }

        static void OnObjectBuilderChangedForConsumer(ObjectBuilderChangedEventArgs args)
        {
            if (args.ChangeMode == ObjectBuilderChangeMode.Activate)
                Console.WriteLine("The ServiceConsumer is activated...");
            else if (args.ChangeMode == ObjectBuilderChangeMode.Deactivate)
                Console.WriteLine("The ServiceConsumer is deactivated...");
        }

        #endregion

        #region ObjectCollectionObserver
        
        private static Tree _tree;

        static void RunObjectCollectionObserverTest()
        {
            IObjectRegistration<Node> nodeRegistration;
            IObjectCollectionObserver<Node> nodesObserver;

            // Register and commit
            _container.Register<Node, Node1>().Return(out nodeRegistration);
            _container.Register<Node, Node2>();
            _container.Register<Node, Node3>();
            _container.Register<Node, Node4>();
            _container.CommitRegistrations();

            // Try to get the ObjectObserver and hook events
            if (_container.TryGetObserver(out nodesObserver))
                nodesObserver.Changed += OnObjectBuilderChangedForNodeCollection;

            var nodes = _container.ResolveAll(nodesObserver);
            _tree = new Tree(nodes);
            PrintTree();

            // Unregister the Service. The observers should receive the notification later.
            _container.Unregister(nodeRegistration);
            PrintTree();

            _container.Register<Node, Node1>();
            _container.CommitRegistrations();
            PrintTree();

            // Dispose the observer
            nodesObserver.Dispose();
        }

        static void PrintTree()
        {
            Console.WriteLine();
            foreach (var node in _tree.ChildNodes)
                Console.WriteLine(node.Name);
        }

        static void OnObjectBuilderChangedForNodeCollection(ObjectBuilderCollectionChangedEventArgs args)
        {
            if (args.ChangeMode == ObjectBuilderCollectionChangeMode.Add)
            {
                var node = (Node)_container.Resolve(args.ObjectBuilder, null);
                _tree.ChildNodes.Add(node);
            }
            else if (args.ChangeMode == ObjectBuilderCollectionChangeMode.Remove)
            {
                _tree.ChildNodes.RemoveAt(args.Position);
            }
        }
        
        #endregion
    }
}
